// 版权所有2010 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 包pprof以pprof可视化工具预期的格式写入运行时分析数据。
// 
// 评测围棋程序
// 
// 评测围棋程序的第一步是启用评测。
// 对使用标准测试构建的评测基准的支持
// 包内置于go测试中。例如，以下命令
// 在当前目录中运行基准测试，并将CPU和
// 内存配置文件写入CPU。教授和mem。教授：
// 
// go test-cpu文件cpu。Memprof-mem。bench教授。
// 
// 若要为独立程序添加等效的评测支持，请在主函数中添加如下代码：
// 
// var cpupfile=flag。字符串（“cpuprofile”，“将cpu配置文件写入`文件'”）
// var memprofile=flag。String（“memprofile”，“write memory profile to`file`”）
// 
// func main（）{
// flag.Parse（）
// if*cpuprofile！=“{
// f，err os.Create（*cpuprofile）
// if err nil{
// log.Fatal:”无法创建CPU配置文件：“，err
// }
// 延迟f.Close（）
// 如果错误：=pprof。StartCPUProfile（f）；呃！=nil{
// log.Fatal（“无法启动CPU配置文件：”，错误）
// }
// /延迟pprof。stopcpurpfile（）
// }
// 
// 。。。节目的其他部分。。。
// 
// if*memprofile！=“{
// f，err:=os.Create（*memprofile）
// if err！=nil{
// log.Fatal”（“无法创建内存配置文件：”，err）
// }
// defer f.Close（）
// runtime省略了错误处理。GC（）
// 如果错误：=pprof。写eapprofile（f）；呃！=nil{
// log.Fatal（“无法写入内存配置文件：”，err）
// }
// }
// }
// 
// 还有一个标准的HTTP接口来分析数据。添加
// 以下行将在/debug/pprof/
// URL下安装处理程序以下载实时配置文件：
// 
// 导入“net/http/pprof”
// 
// 有关详细信息，请参阅net/http/pprof包。
// 
// 然后可以使用pprof工具可视化配置文件：
// 
// go工具pprof cpu。prof 
// 
// pprof命令行中有许多命令可用。
// 常用命令包括“top”，它打印了
// top程序热点的摘要，以及“web”，它打开了热点及其调用图的交互式图形
// 。有关
// 所有命令的信息，请使用“帮助”。
// 
// 有关pprof的更多信息，请参阅
// https:
package pprof

import (
	"bufio"
	"bytes"
	"fmt"
	"internal/abi"
	"io"
	"runtime"
	"sort"
	"strings"
	"sync"
	"text/tabwriter"
	"time"
	"unsafe"
)

// 错误（rsc）：配置文件的性能取决于用于生成它们的内核支持。
// 参见https:

// 配置文件是堆栈跟踪的集合，显示导致特定事件（如分配）实例的调用序列。
// 包可以创建和维护自己的配置文件；最常见的
// 用于跟踪必须显式关闭的资源，例如文件
// 或网络连接。
// 
// 可以同时从多个Goroutine调用配置文件的方法。wen jian defg
// 
// goroutine-所有当前goroutine的堆栈跟踪
// heap-活动对象内存分配的采样
// allocs-所有过去内存分配的采样
// threadcreate-导致创建新操作系统线程的堆栈跟踪
// block-导致同步原语阻塞的堆栈跟踪
// mutex-竞争互斥体持有者的堆栈跟踪
// 
// 这些预定义的配置文件在显式
// 添加或删除方法调用时保持自身和恐慌。
// 
// 堆配置文件报告截至最近完成的
// 垃圾收集的统计信息；它省去了最近的分配，以避免将
// 配置文件从实时数据转向垃圾。
// 如果根本没有垃圾回收，堆配置文件将报告所有已知的分配。此异常主要对运行
// 且未启用垃圾收集（通常用于调试）的程序有帮助。
// 
// 堆配置文件跟踪
// 中所有活动对象的分配位置，以及应用程序内存和自程序启动以来分配的所有对象的分配位置。
// Pprof的-inuse_space、-inuse_objects、-alloc_space和-alloc_objects 
// 选择要显示的标志，默认为-inuse_space（活动对象，
// 按大小缩放）。
// 
// allocs配置文件与堆配置文件相同，但将默认的
// pprof显示更改为-alloc_space，即自
// 程序启动以来分配的总字节数（包括垃圾收集的字节）。
// 
// CPU配置文件不能作为配置文件使用。它有一个特殊的API，即
// StartCPUProfile和StopCPUProfile函数，因为它在评测期间将
// 输出流式传输给编写器。
// 
type Profile struct {
	name  string
	mu    sync.Mutex
	m     map[any][]uintptr
	count func() int
	write func(io.Writer, int) error
}

// 配置文件记录所有注册的配置文件。
var profiles struct {
	mu sync.Mutex
	m  map[string]*Profile
}

var goroutineProfile = &Profile{
	name:  "goroutine",
	count: countGoroutine,
	write: writeGoroutine,
}

var threadcreateProfile = &Profile{
	name:  "threadcreate",
	count: countThreadCreate,
	write: writeThreadCreate,
}

var heapProfile = &Profile{
	name:  "heap",
	count: countHeap,
	write: writeHeap,
}

var allocsProfile = &Profile{
	name:  "allocs",
	count: countHeap, // 与堆配置文件相同
	write: writeAlloc,
}

var blockProfile = &Profile{
	name:  "block",
	count: countBlock,
	write: writeBlock,
}

var mutexProfile = &Profile{
	name:  "mutex",
	count: countMutex,
	write: writeMutex,
}

func lockProfiles() {
	profiles.mu.Lock()
	if profiles.m == nil {
		// 初始内置配置文件。
		profiles.m = map[string]*Profile{
			"goroutine":    goroutineProfile,
			"threadcreate": threadcreateProfile,
			"heap":         heapProfile,
			"allocs":       allocsProfile,
			"block":        blockProfile,
			"mutex":        mutexProfile,
		}
	}
}

func unlockProfiles() {
	profiles.mu.Unlock()
}

// NewProfile使用给定的名称创建一个新的配置文件。
// 如果已经存在具有该名称的配置文件，新配置文件就会恐慌。
// 惯例是使用“导入/路径”创建
// 的前缀为每个包提供单独的名称空间。
// 为了与读取pprof数据的各种工具兼容，
// 配置文件名称不应包含空格。
func NewProfile(name string) *Profile {
	lockProfiles()
	defer unlockProfiles()
	if name == "" {
		panic("pprof: NewProfile with empty name")
	}
	if profiles.m[name] != nil {
		panic("pprof: NewProfile name already in use: " + name)
	}
	p := &Profile{
		name: name,
		m:    map[any][]uintptr{},
	}
	profiles.m[name] = p
	return p
}

// Lookup返回具有给定名称的配置文件，如果不存在此类配置文件，则返回nil。
func Lookup(name string) *Profile {
	lockProfiles()
	defer unlockProfiles()
	return profiles.m[name]
}

// Profiles返回所有已知概要文件的一个片段，按名称排序。
func Profiles() []*Profile {
	lockProfiles()
	defer unlockProfiles()

	all := make([]*Profile, 0, len(profiles.m))
	for _, p := range profiles.m {
		all = append(all, p)
	}

	sort.Slice(all, func(i, j int) bool { return all[i].name < all[j].name })
	return all
}

// Name返回此配置文件的名称，可以将其传递给Lookup以重新获取配置文件。
func (p *Profile) Name() string {
	return p.name
}

// Count返回配置文件中当前的执行堆栈数。
func (p *Profile) Count() int {
	p.mu.Lock()
	defer p.mu.Unlock()
	if p.count != nil {
		return p.count()
	}
	return len(p.m)
}

// Add将当前执行堆栈添加到配置文件中，并与值关联。
// Add在内部映射中存储值，因此值必须适合用作映射密钥，并且在相应的
// 调用删除之前不会被垃圾收集。如果配置文件已经包含值堆栈，则添加恐慌。
// 
// skip参数的含义与runtime相同。调用方的跳过
// 并控制堆栈跟踪的开始位置。传递skip=0将在调用Add的函数中开始
// 跟踪。例如，给定这个
// 执行堆栈：
// 
// 添加
// 从rpc调用。新客户端
// 从mypkg调用。运行
// 从main调用。main 
// 
// 传递skip=0将在rpc内部添加的调用中开始堆栈跟踪。新客户。
// 传递skip=1将在mypkg内部调用NewClient时开始堆栈跟踪。跑
// 
func (p *Profile) Add(value any, skip int) {
	if p.name == "" {
		panic("pprof: use of uninitialized Profile")
	}
	if p.write != nil {
		panic("pprof: Add called on built-in Profile " + p.name)
	}

	stk := make([]uintptr, 32)
	n := runtime.Callers(skip+1, stk[:])
	stk = stk[:n]
	if len(stk) == 0 {
		// 跳过的值太大，并且没有要记录的堆栈跟踪。
		stk = []uintptr{abi.FuncPCABIInternal(lostProfileEvent)}
	}

	p.mu.Lock()
	defer p.mu.Unlock()
	if p.m[value] != nil {
		panic("pprof: Profile.Add of duplicate value")
	}
	p.m[value] = stk
}

// Remove从配置文件中删除与值关联的执行堆栈。
// 如果该值不在配置文件中，则为不可操作。
func (p *Profile) Remove(value any) {
	p.mu.Lock()
	defer p.mu.Unlock()
	delete(p.m, value)
}

// WriteTo将配置文件的pprof格式快照写入w。
// 如果写入w返回错误，WriteTo将返回该错误。wen dang
// 
// 调试参数启用其他输出。
// Passing debug=0写入中描述的gzip压缩协议缓冲区
// https:
// Passing debug=1编写带有注释的传统文本格式
// 将地址转换为函数名和行号，这样程序员就可以在不使用工具的情况下读取配置文件。
// 
// 预定义的配置文件可能会将意义分配给其他调试值；
// 例如，当打印“goroutine”配置文件时，debug=2意味着
// 打印goroutine堆栈，格式与Go程序在因未恢复的恐慌而死亡时使用的格式相同。
func (p *Profile) WriteTo(w io.Writer, debug int) error {
	if p.name == "" {
		panic("pprof: use of zero Profile")
	}
	if p.write != nil {
		return p.write(w, debug)
	}

	// 在锁定状态下获取一致的快照；然后不加锁地处理。
	p.mu.Lock()
	all := make([][]uintptr, 0, len(p.m))
	for _, stk := range p.m {
		all = append(all, stk)
	}
	p.mu.Unlock()

	// 映射顺序是不确定的；使输出具有确定性。
	sort.Slice(all, func(i, j int) bool {
		t, u := all[i], all[j]
		for k := 0; k < len(t) && k < len(u); k++ {
			if t[k] != u[k] {
				return t[k] < u[k]
			}
		}
		return len(t) < len(u)
	})

	return printCountProfile(w, debug, p.name, stackProfile(all))
}

type stackProfile [][]uintptr

func (x stackProfile) Len() int              { return len(x) }
func (x stackProfile) Stack(i int) []uintptr { return x[i] }
func (x stackProfile) Label(i int) *labelMap { return nil }

// countProfile是一组要打印为计数的堆栈跟踪
// 按堆栈跟踪分组。有多种实现方式：
// 重要的是，我们可以找出有多少道
// 并依次获得每条道。
type countProfile interface {
	Len() int
	Stack(i int) []uintptr
	Label(i int) *labelMap
}

// printCountCycleProfile输出块配置文件记录（用于块或互斥配置文件）
// 。从周期计数到持续时间
// 的转换已经完成，因为proto需要的是计数和时间（纳秒），而不是计数
// 以及块、争用配置文件的周期数。
// 可能的“scaler”函数是scaleBlockProfile和ScaleTextProfile。
func printCountCycleProfile(w io.Writer, countName, cycleName string, scaler func(int64, float64) (int64, float64), records []runtime.BlockProfileRecord) error {
	// protobuf格式的输出配置文件。
	b := newProfileBuilder(w)
	b.pbValueType(tagProfile_PeriodType, countName, "count")
	b.pb.int64Opt(tagProfile_Period, 1)
	b.pbValueType(tagProfile_SampleType, countName, "count")
	b.pbValueType(tagProfile_SampleType, cycleName, "nanoseconds")

	cpuGHz := float64(runtime_cyclesPerSecond()) / 1e9

	values := []int64{0, 0}
	var locs []uint64
	for _, r := range records {
		count, nanosec := scaler(r.Count, float64(r.Cycles)/cpuGHz)
		values[0] = count
		values[1] = int64(nanosec)
		// 对于计数配置文件，所有堆栈地址都是
		// 返回PC，这是appendLocsForStack所期望的。
		locs = b.appendLocsForStack(locs[:0], r.Stack())
		b.pbSample(values, locs, nil)
	}
	b.build()
	return nil
}

// printCountProfile在指定的调试级别打印countProfile。
// 除非debug为非零，否则配置文件将采用压缩的原始格式。
func printCountProfile(w io.Writer, debug int, name string, p countProfile) error {
	// 每个堆栈的生成计数。
	var buf bytes.Buffer
	key := func(stk []uintptr, lbls *labelMap) string {
		buf.Reset()
		fmt.Fprintf(&buf, "@")
		for _, pc := range stk {
			fmt.Fprintf(&buf, " %#x", pc)
		}
		if lbls != nil {
			buf.WriteString("\n# labels: ")
			buf.WriteString(lbls.String())
		}
		return buf.String()
	}
	count := map[string]int{}
	index := map[string]int{}
	var keys []string
	n := p.Len()
	for i := 0; i < n; i++ {
		k := key(p.Stack(i), p.Label(i))
		if count[k] == 0 {
			index[k] = i
			keys = append(keys, k)
		}
		count[k]++
	}

	sort.Sort(&keysByCount{keys, count})

	if debug > 0 {
		// 以旧格式打印调试配置文件
		tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
		fmt.Fprintf(tw, "%s profile: total %d\n", name, p.Len())
		for _, k := range keys {
			fmt.Fprintf(tw, "%d %s\n", count[k], k)
			printStackRecord(tw, p.Stack(index[k]), false)
		}
		return tw.Flush()
	}

	// 以protobuf格式输出配置文件。
	b := newProfileBuilder(w)
	b.pbValueType(tagProfile_PeriodType, name, "count")
	b.pb.int64Opt(tagProfile_Period, 1)
	b.pbValueType(tagProfile_SampleType, name, "count")

	values := []int64{0}
	var locs []uint64
	for _, k := range keys {
		values[0] = int64(count[k])
		// 对于计数配置文件，所有堆栈地址都是
		// 返回PC，这是appendLocsForStack所期望的。
		locs = b.appendLocsForStack(locs[:0], p.Stack(index[k]))
		idx := index[k]
		var labels func()
		if p.Label(idx) != nil {
			labels = func() {
				for k, v := range *p.Label(idx) {
					b.pbLabel(tagSample_Label, k, v, 0)
				}
			}
		}
		b.pbSample(values, locs, labels)
	}
	b.build()
	return nil
}

// keysByCount首先对计数较高的键进行排序，按键字符串顺序打破联系。
type keysByCount struct {
	keys  []string
	count map[string]int
}

func (x *keysByCount) Len() int      { return len(x.keys) }
func (x *keysByCount) Swap(i, j int) { x.keys[i], x.keys[j] = x.keys[j], x.keys[i] }
func (x *keysByCount) Less(i, j int) bool {
	ki, kj := x.keys[i], x.keys[j]
	ci, cj := x.count[ki], x.count[kj]
	if ci != cj {
		return ci > cj
	}
	return ki < kj
}

// printStackRecord为单个堆栈跟踪打印函数+源行信息
// 。
func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) {
	show := allFrames
	frames := runtime.CallersFrames(stk)
	for {
		frame, more := frames.Next()
		name := frame.Function
		if name == "" {
			show = true
			fmt.Fprintf(w, "#\t%#x\n", frame.PC)
		} else if name != "runtime.goexit" && (show || !strings.HasPrefix(name, "runtime.")) {
			// 隐藏运行时。goexit和任何一个运行时函数。
			// 这主要用于分配跟踪。
			show = true
			fmt.Fprintf(w, "#\t%#x\t%s+%#x\t%s:%d\n", frame.PC, name, frame.PC-frame.Entry, frame.File, frame.Line)
		}
		if !more {
			break
		}
	}
	if !show {
		// 我们没有打印任何内容；再做一次，
		// 这次包括运行时函数。
		printStackRecord(w, stk, true)
		return
	}
	fmt.Fprintf(w, "\n")
}

// 系统配置文件的接口。

// WriteHeapProfile是查找（“堆”）的缩写。写入（w，0）。
// 为向后兼容而保留。
func WriteHeapProfile(w io.Writer) error {
	return writeHeap(w, 0)
}

// countHeap返回堆配置文件中的记录数。
func countHeap() int {
	n, _ := runtime.MemProfile(nil, true)
	return n
}

// writeHeap将当前运行时堆配置文件写入w。
func writeHeap(w io.Writer, debug int) error {
	return writeHeapInternal(w, debug, "")
}

// writeAlloc将当前运行时堆配置文件写入w 
// ，总分配空间作为默认样本类型。
func writeAlloc(w io.Writer, debug int) error {
	return writeHeapInternal(w, debug, "alloc_space")
}

func writeHeapInternal(w io.Writer, debug int, defaultSampleType string) error {
	var memStats *runtime.MemStats
	if debug != 0 {
		// 首先读取mem统计数据，这样我们的其他分配
		// 就不会出现在统计数据中。
		memStats = new(runtime.MemStats)
		runtime.ReadMemStats(memStats)
	}

	// 找出有多少条记录（MemProfile（nil，true）），
	// 分配那么多记录，并获取数据。
	// 这两个电话之间可能会有更多的记录，所以为了安全分配一些额外的记录
	// 如果我们非常不走运，请再试一次。
	// 在常见情况下，循环只应执行一次迭代。
	var p []runtime.MemProfileRecord
	n, ok := runtime.MemProfile(nil, true)
	for {
		// 为稍微大一点的配置文件分配空间，
		// 以防在调用MemProfile后添加了更多条目。
		p = make([]runtime.MemProfileRecord, n+50)
		n, ok = runtime.MemProfile(p, true)
		if ok {
			p = p[0:n]
			break
		}
		// 个人资料增长；再试一次。ABCFDG 
	}

	if debug == 0 {
		return writeHeapProto(w, p, int64(runtime.MemProfileRate), defaultSampleType)
	}

	sort.Slice(p, func(i, j int) bool { return p[i].InUseBytes() > p[j].InUseBytes() })

	b := bufio.NewWriter(w)
	tw := tabwriter.NewWriter(b, 1, 8, 1, '\t', 0)
	w = tw

	var total runtime.MemProfileRecord
	for i := range p {
		r := &p[i]
		total.AllocBytes += r.AllocBytes
		total.AllocObjects += r.AllocObjects
		total.FreeBytes += r.FreeBytes
		total.FreeObjects += r.FreeObjects
	}

	fmt.Fprintf(w, "heap profile: %d: %d [%d: %d] @ heap/%d\n",
		total.InUseObjects(), total.InUseBytes(),
		total.AllocObjects, total.AllocBytes,
		2*runtime.MemProfileRate)

	for i := range p {
		r := &p[i]
		fmt.Fprintf(w, "%d: %d [%d: %d] @",
			r.InUseObjects(), r.InUseBytes(),
			r.AllocObjects, r.AllocBytes)
		for _, pc := range r.Stack() {
			fmt.Fprintf(w, " %#x", pc)
		}
		fmt.Fprintf(w, "\n")
		printStackRecord(w, r.Stack(), false)
	}

	// 也打印memstats信息。
	// Pprof将被忽略，但对人们有用
	s := memStats
	fmt.Fprintf(w, "\n# runtime.MemStats\n")
	fmt.Fprintf(w, "# Alloc = %d\n", s.Alloc)
	fmt.Fprintf(w, "# TotalAlloc = %d\n", s.TotalAlloc)
	fmt.Fprintf(w, "# Sys = %d\n", s.Sys)
	fmt.Fprintf(w, "# Lookups = %d\n", s.Lookups)
	fmt.Fprintf(w, "# Mallocs = %d\n", s.Mallocs)
	fmt.Fprintf(w, "# Frees = %d\n", s.Frees)

	fmt.Fprintf(w, "# HeapAlloc = %d\n", s.HeapAlloc)
	fmt.Fprintf(w, "# HeapSys = %d\n", s.HeapSys)
	fmt.Fprintf(w, "# HeapIdle = %d\n", s.HeapIdle)
	fmt.Fprintf(w, "# HeapInuse = %d\n", s.HeapInuse)
	fmt.Fprintf(w, "# HeapReleased = %d\n", s.HeapReleased)
	fmt.Fprintf(w, "# HeapObjects = %d\n", s.HeapObjects)

	fmt.Fprintf(w, "# Stack = %d / %d\n", s.StackInuse, s.StackSys)
	fmt.Fprintf(w, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys)
	fmt.Fprintf(w, "# MCache = %d / %d\n", s.MCacheInuse, s.MCacheSys)
	fmt.Fprintf(w, "# BuckHashSys = %d\n", s.BuckHashSys)
	fmt.Fprintf(w, "# GCSys = %d\n", s.GCSys)
	fmt.Fprintf(w, "# OtherSys = %d\n", s.OtherSys)

	fmt.Fprintf(w, "# NextGC = %d\n", s.NextGC)
	fmt.Fprintf(w, "# LastGC = %d\n", s.LastGC)
	fmt.Fprintf(w, "# PauseNs = %d\n", s.PauseNs)
	fmt.Fprintf(w, "# PauseEnd = %d\n", s.PauseEnd)
	fmt.Fprintf(w, "# NumGC = %d\n", s.NumGC)
	fmt.Fprintf(w, "# NumForcedGC = %d\n", s.NumForcedGC)
	fmt.Fprintf(w, "# GCCPUFraction = %v\n", s.GCCPUFraction)
	fmt.Fprintf(w, "# DebugGC = %v\n", s.DebugGC)

	// 还将清除受支持平台上的MaxRSS。
	addMaxRSS(w)

	tw.Flush()
	return b.Flush()
}

// countThreadCreate返回当前ThreadCreateProfile的大小。
func countThreadCreate() int {
	n, _ := runtime.ThreadCreateProfile(nil)
	return n
}

// writeThreadCreate将当前运行时的ThreadCreateProfile写入w。
func writeThreadCreate(w io.Writer, debug int) error {
	// 直到https:
	// ThreadCreateProfile，因为当我们
	// 没有任何堆栈跟踪时，跟踪标签没有意义。
	return writeRuntimeProfile(w, debug, "threadcreate", func(p []runtime.StackRecord, _ []unsafe.Pointer) (n int, ok bool) {
		return runtime.ThreadCreateProfile(p)
	})
}

// countGoroutine返回goroutine的数量。
func countGoroutine() int {
	return runtime.NumGoroutine()
}

// runtime_goroutineProfileWithLabels在runtime/mprof中定义。go 
func runtime_goroutineProfileWithLabels(p []runtime.StackRecord, labels []unsafe.Pointer) (n int, ok bool)

// writeGoroutine将当前运行时goroutine文件写入w。
func writeGoroutine(w io.Writer, debug int) error {
	if debug >= 2 {
		return writeGoroutineStacks(w)
	}
	return writeRuntimeProfile(w, debug, "goroutine", runtime_goroutineProfileWithLabels)
}

func writeGoroutineStacks(w io.Writer) error {
	// 我们不知道收集所有goroutine需要多大的缓冲区。从1MB开始，尝试几次，每次加倍。
	// 如果64MB不够，请放弃并使用截断的跟踪。
	buf := make([]byte, 1<<20)
	for i := 0; ; i++ {
		n := runtime.Stack(buf, true)
		if n < len(buf) {
			buf = buf[:n]
			break
		}
		if len(buf) >= 64<<20 {
			// 已填充64MB-到此为止。
			break
		}
		buf = make([]byte, 2*len(buf))
	}
	_, err := w.Write(buf)
	return err
}

func writeRuntimeProfile(w io.Writer, debug int, name string, fetch func([]runtime.StackRecord, []unsafe.Pointer) (int, bool)) error {
	// 找出有多少条记录（fetch（nil）），
	// 分配那么多记录，并获取数据。
	// 这两个电话之间可能会有更多的记录，所以为了安全分配一些额外的记录
	// 如果我们非常不走运，请再试一次。
	// 在常见情况下，循环应该只执行一次迭代。
	var p []runtime.StackRecord
	var labels []unsafe.Pointer
	n, ok := fetch(nil, nil)
	for {
		// 为稍大的配置文件分配空间，
		// 以防在调用ThreadProfile后添加了更多条目
		// 。
		p = make([]runtime.StackRecord, n+10)
		labels = make([]unsafe.Pointer, n+10)
		n, ok = fetch(p, labels)
		if ok {
			p = p[0:n]
			break
		}
		// 个人资料增长；再试一次。
	}

	return printCountProfile(w, debug, name, &runtimeProfile{p, labels})
}

type runtimeProfile struct {
	stk    []runtime.StackRecord
	labels []unsafe.Pointer
}

func (p *runtimeProfile) Len() int              { return len(p.stk) }
func (p *runtimeProfile) Stack(i int) []uintptr { return p.stk[i].Stack() }
func (p *runtimeProfile) Label(i int) *labelMap { return (*labelMap)(p.labels[i]) }

var cpu struct {
	sync.Mutex
	profiling bool
	done      chan bool
}

// StartCPUProfile为当前进程启用CPU配置文件。
// 评测时，配置文件将被缓冲并写入w。
// 如果已启用评测，StartCPupProfile将返回错误。
// 
// 在类似Unix的系统上，默认情况下，StartCPupProfile不适用于
// Go使用-buildmode=c-archive或-buildmode=c-shared生成的代码。
// StartCPupProfile依赖于SIGPROF信号，但该信号将被发送到主程序的SIGPROF信号处理程序（如果有）
// 而不是Go使用的信号处理程序。要使其工作，请调用os/signal。通知
// 进行系统调用。SIGPROF，但请注意，这样做可能会破坏主程序正在进行的任何分析
// 。
func StartCPUProfile(w io.Writer) error {
	// 运行时例程允许可变的分析速率，
	// 但在实践中，操作系统无法以超过约500 Hz的频率触发信号
	// 并且我们对
	// 信号的处理并不便宜（主要是获取堆栈跟踪）。
	// 100 Hz是一个合理的选择：它足够频繁，可以产生有用的数据，足够罕见，不会使
	// 系统陷入困境，并且是一个很好的整数，可以方便地将样本计数转换为秒。我们不要求每个客户机指定频率，而是硬编码。
	const hz = 100

	cpu.Lock()
	defer cpu.Unlock()
	if cpu.done == nil {
		cpu.done = make(chan bool)
	}
	// 仔细检查。
	if cpu.profiling {
		return fmt.Errorf("cpu profiling already in use")
	}
	cpu.profiling = true
	runtime.SetCPUProfileRate(hz)
	go profileWriter(w)
	return nil
}

// 运行时提供的readProfile返回下一块
// 二进制CPU评测堆栈跟踪数据，直到数据可用为止。
// 如果关闭分析功能，并且返回了在其打开时累积的所有分析数据，则readProfile返回eof=true。
// 在再次调用readProfile之前，调用者必须保存返回的数据和标记。
func readProfile() (data []uint64, tags []unsafe.Pointer, eof bool)

func profileWriter(w io.Writer) {
	b := newProfileBuilder(w)
	var err error
	for {
		time.Sleep(100 * time.Millisecond)
		data, tags, eof := readProfile()
		if e := b.addCPUData(data, tags); e != nil && err == nil {
			err = e
		}
		if eof {
			break
		}
	}
	if err != nil {
		// 运行时不应生成无效或截断的配置文件。
		// 它会删除无法放入日志缓冲区的记录。
		panic("runtime/pprof: converting profile: " + err.Error())
	}
	b.build()
	cpu.done <- true
}

// stopcuprofile停止当前的CPU配置文件（如果有）。
// stopcuprofile仅在
// 配置文件的所有写入操作完成后返回。
func StopCPUProfile() {
	cpu.Lock()
	defer cpu.Unlock()

	if !cpu.profiling {
		return
	}
	cpu.profiling = false
	runtime.SetCPUProfileRate(0)
	<-cpu.done
}

// countBlock返回阻塞配置文件中的记录数。
func countBlock() int {
	n, _ := runtime.BlockProfile(nil)
	return n
}

// countMutex返回互斥配置文件中的记录数。
func countMutex() int {
	n, _ := runtime.MutexProfile(nil)
	return n
}

// writeBlock将当前阻塞配置文件写入w。
func writeBlock(w io.Writer, debug int) error {
	return writeProfileInternal(w, debug, "contention", runtime.BlockProfile, scaleBlockProfile)
}

func scaleBlockProfile(cnt int64, ns float64) (int64, float64) {
	// 不执行任何操作。
	// 目前的区块剖面采样方式使未采样数的计算变得困难。遗留块
	// 配置文件解析不会尝试缩放或取消采样。
	return cnt, ns
}

// writeMutex将当前互斥配置文件写入w。
func writeMutex(w io.Writer, debug int) error {
	return writeProfileInternal(w, debug, "mutex", runtime.MutexProfile, scaleMutexProfile)
}

// writepProfileInternal根据传递的参数
func writeProfileInternal(w io.Writer, debug int, name string, runtimeProfile func([]runtime.BlockProfileRecord) (int, bool), scaleProfile func(int64, float64) (int64, float64)) error {
	var p []runtime.BlockProfileRecord
	n, ok := runtimeProfile(nil)
	for {
		p = make([]runtime.BlockProfileRecord, n+50)
		n, ok = runtimeProfile(p)
		if ok {
			p = p[:n]
			break
		}
	}

	sort.Slice(p, func(i, j int) bool { return p[i].Cycles > p[j].Cycles })

	if debug <= 0 {
		return printCountCycleProfile(w, "contentions", "delay", scaleProfile, p)
	}

	b := bufio.NewWriter(w)
	tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
	w = tw

	fmt.Fprintf(w, "--- %v:\n", name)
	fmt.Fprintf(w, "cycles/second=%v\n", runtime_cyclesPerSecond())
	if name == "mutex" {
		fmt.Fprintf(w, "sampling period=%d\n", runtime.SetMutexProfileFraction(-1))
	}
	for i := range p {
		r := &p[i]
		fmt.Fprintf(w, "%v %v @", r.Cycles, r.Count)
		for _, pc := range r.Stack() {
			fmt.Fprintf(w, " %#x", pc)
		}
		fmt.Fprint(w, "\n")
		if debug > 0 {
			printStackRecord(w, r.Stack(), true)
		}
	}

	if tw != nil {
		tw.Flush()
	}
	return b.Flush()
}

func scaleMutexProfile(cnt int64, ns float64) (int64, float64) {
	period := runtime.SetMutexProfileFraction(-1)
	return cnt * int64(period), ns * float64(period)
}

func runtime_cyclesPerSecond() int64
